package br.com.bancodecurriculos.pessoa.fisica.dao.impl;

import java.io.IOException;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import br.com.bancodecurriculos.controleacesso.dominio.Perfil;
import br.com.bancodecurriculos.controleacesso.dominio.Usuario;
import br.com.bancodecurriculos.pessoa.comum.dao.IEmailDAOHelper;
import br.com.bancodecurriculos.pessoa.comum.dao.IEnderecoDAOHelper;
import br.com.bancodecurriculos.pessoa.comum.dao.ITelefoneDAOHelper;
import br.com.bancodecurriculos.pessoa.comum.dominio.Email;
import br.com.bancodecurriculos.pessoa.comum.dominio.Endereco;
import br.com.bancodecurriculos.pessoa.comum.dominio.Telefone;
import br.com.bancodecurriculos.pessoa.fisica.dao.IPessoaFisicaDAO;
import br.com.bancodecurriculos.pessoa.fisica.dominio.PessoaFisica;
import br.com.bancodecurriculos.pessoa.fisica.dominio.PessoaFisica.EstadoCivil;
import br.com.bancodecurriculos.util.dao.JDBCConnectionFactory;
import br.com.bancodecurriculos.util.exception.RegistroExistenteException;
import br.com.bancodecurriculos.util.exception.RegistroInexistenteException;

public class PessoaFisicaDAO implements IPessoaFisicaDAO {

	private static final String SQL_INSERT_PESSOAFISICA = "INSERT INTO pessoafisica (cpf, nome, datanascimento, sexo,"
			+ " estadocivil, datacadastro, id_usuario) VALUES (?, ?, ?, ?, ?, ?, ?)";
	private static final String SQL_UPDATE_PESSOAFISICA = "UPDATE pessoafisica SET cpf = ?, nome = ?, datanascimento = ?, sexo = ?,"
			+ " estadocivil = ? WHERE id = ?";
	private static final String SQL_GET_PESSOAFISICA_BY_CPF = "SELECT pessoafisica.id AS id, pessoafisica.cpf AS cpf, pessoafisica.nome AS nome,"
			+ " pessoafisica.datanascimento AS datanascimento, pessoafisica.sexo AS sexo, pessoafisica.estadocivil AS estadocivil,"
			+ " pessoafisica.datacadastro AS datacadastro, usuario.id AS id_usuario, usuario.login AS login_usuario, usuario.senha AS senha_usuario,"
			+ " perfil.id AS id_perfil, perfil.nome AS nome_perfil FROM pessoafisica INNER JOIN usuario ON pessoafisica.id_usuario = usuario.id,"
			+ " INNER JOIN perfil ON usuario.id_perfil = perifl.id WHERE pessoafisica.cpf = ?";
	private static final String SQL_DELETE_PESSOAFISICA = "DELETE FROM pessoafisica WHERE id = ?";

	private static final String SQL_INSERT_PESSOAFISICA_ENDERECO = "INSERT INTO pessoafisica_endereco (id_pessoafisica, id_endereco)"
			+ " VALUES (?, ?)";
	private static final String SQL_GET_ENDERECOS_BY_ID_PESSOAFISICA = "SELECT id_endereco FROM pessoafisica_endereco WHERE id_pessoafisica = ?";

	private IEnderecoDAOHelper enderecoDAOHelper;
	private ITelefoneDAOHelper telefoneDAOHelper;
	private IEmailDAOHelper emailDAOHelper;

	public PessoaFisicaDAO(IEnderecoDAOHelper enderecoDAOHelper,
			ITelefoneDAOHelper telefoneDAOHelper, IEmailDAOHelper emailDAOHelper) {
		this.enderecoDAOHelper = enderecoDAOHelper;
		this.telefoneDAOHelper = telefoneDAOHelper;
		this.emailDAOHelper = emailDAOHelper;
	}

	/**
	 * PROBLEM: tratar exceptions em telas diferentes
	 */
	@Override
	public void save(PessoaFisica t) throws ClassNotFoundException,
			SQLException, RegistroExistenteException, IOException {
		PreparedStatement preparedStatement = JDBCConnectionFactory
				.getInstance()
				.getConnection()
				.prepareStatement(SQL_INSERT_PESSOAFISICA,
						PreparedStatement.RETURN_GENERATED_KEYS);
		preparedStatement.setString(1, t.getCpf());
		preparedStatement.setString(2, t.getNome());
		preparedStatement.setDate(3, (Date) t.getDataNascimento());
		preparedStatement.setString(4, String.valueOf(t.getSexo()));
		preparedStatement.setString(5, t.getEstadoCivil().getNome());
		preparedStatement.setDate(6, (Date) t.getDataCadastro());
		preparedStatement.setLong(7, t.getUsuario().getId());
		ResultSet resultSet = preparedStatement.getGeneratedKeys();
		if (resultSet.next()) {
			t.setId(resultSet.getLong("id"));
		}
		preparedStatement.close();
		resultSet.close();
		this.salvarEnderecos(t);
		this.salvarTelefones(t);
		this.salvarEmails(t);
	}

	@Override
	public void update(PessoaFisica t) throws ClassNotFoundException,
			SQLException, RegistroInexistenteException, IOException {
		PreparedStatement preparedStatement = JDBCConnectionFactory
				.getInstance().getConnection()
				.prepareStatement(SQL_UPDATE_PESSOAFISICA);
		preparedStatement.setString(1, t.getCpf());
		preparedStatement.setString(2, t.getNome());
		preparedStatement.setDate(3, (Date) t.getDataNascimento());
		preparedStatement.setString(4, String.valueOf(t.getSexo()));
		preparedStatement.setString(5, t.getEstadoCivil().name());
		preparedStatement.setLong(6, t.getId());
		preparedStatement.executeQuery();
		this.atualizarEnderecos(t);
		this.atualizarTelefones(t);
		this.atualizarEmails(t);
	}

	@Override
	public PessoaFisica getById(Long id) throws ClassNotFoundException,
			SQLException, IOException {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public PessoaFisica getByCpf(String cpf) throws ClassNotFoundException,
			SQLException, IOException {
		PessoaFisica pessoaFisica = null;
		PreparedStatement preparedStatement = JDBCConnectionFactory
				.getInstance().getConnection()
				.prepareStatement(SQL_GET_PESSOAFISICA_BY_CPF);
		preparedStatement.setString(1, cpf);
		ResultSet resultSet = preparedStatement.executeQuery();
		pessoaFisica = this.popularPessoaFisica(resultSet);
		return pessoaFisica;
	}

	@Override
	public void delete(Long id) throws ClassNotFoundException, SQLException,
			RegistroInexistenteException, IOException {
		PreparedStatement preparedStatement = JDBCConnectionFactory
				.getInstance().getConnection()
				.prepareStatement(SQL_DELETE_PESSOAFISICA);
		preparedStatement.setLong(1, id);
		preparedStatement.executeUpdate();
		preparedStatement.close();
	}

	@Override
	public IEnderecoDAOHelper getEnderecoDAOHelper() {
		return enderecoDAOHelper;
	}

	@Override
	public ITelefoneDAOHelper getTelefoneDAOHelper() {
		return telefoneDAOHelper;
	}

	@Override
	public IEmailDAOHelper getEmailDAOHelper() {
		return emailDAOHelper;
	}

	private void salvarEnderecos(PessoaFisica pessoaFisica)
			throws ClassNotFoundException, SQLException,
			RegistroExistenteException, IOException {
		List<Endereco> enderecos = pessoaFisica.getEnderecos();
		for (Endereco endereco : enderecos) {
			long idEndereco = this.enderecoDAOHelper.saveReturningId(endereco);
			this.salvarPessoaFisicaEndereco(pessoaFisica.getId(), idEndereco);
		}
	}

	private void salvarPessoaFisicaEndereco(long idPessoaFisica, long idEndereco)
			throws ClassNotFoundException, SQLException, IOException {
		PreparedStatement preparedStatement = JDBCConnectionFactory
				.getInstance().getConnection()
				.prepareStatement(SQL_INSERT_PESSOAFISICA_ENDERECO);
		preparedStatement.setLong(1, idPessoaFisica);
		preparedStatement.setLong(2, idEndereco);
		preparedStatement.executeUpdate();
		preparedStatement.close();
	}

	private void salvarTelefones(PessoaFisica pessoaFisica)
			throws ClassNotFoundException, SQLException,
			RegistroExistenteException, IOException {
		List<Telefone> telefones = pessoaFisica.getTelefones();
		for (Telefone telefone : telefones) {
			this.telefoneDAOHelper.save(telefone, pessoaFisica.getId());
		}
	}

	private void salvarEmails(PessoaFisica pessoaFisica)
			throws ClassNotFoundException, SQLException, IOException,
			RegistroExistenteException {
		List<Email> emails = pessoaFisica.getEmails();
		for (Email email : emails) {
			this.emailDAOHelper.save(email, pessoaFisica.getId());
		}
	}

	private void atualizarEnderecos(PessoaFisica pessoaFisica)
			throws ClassNotFoundException, SQLException, IOException,
			RegistroInexistenteException {
		List<Endereco> enderecos = pessoaFisica.getEnderecos();
		for (Endereco endereco : enderecos) {
			this.enderecoDAOHelper.update(endereco);
		}
	}

	private void atualizarTelefones(PessoaFisica pessoaFisica)
			throws ClassNotFoundException, SQLException, IOException,
			RegistroInexistenteException {
		List<Telefone> telefones = pessoaFisica.getTelefones();
		for (Telefone telefone : telefones) {
			this.telefoneDAOHelper.update(telefone);
		}
	}

	private void atualizarEmails(PessoaFisica pessoaFisica)
			throws ClassNotFoundException, SQLException, IOException,
			RegistroInexistenteException {
		List<Email> emails = pessoaFisica.getEmails();
		for (Email email : emails) {
			this.emailDAOHelper.update(email);
		}
	}

	private PessoaFisica popularPessoaFisica(ResultSet resultSet)
			throws SQLException, ClassNotFoundException, IOException {
		PessoaFisica pessoaFisica = null;
		if (resultSet.next()) {
			Perfil perfil = new Perfil();
			perfil.setId(resultSet.getLong("id_perfil"));
			perfil.setNome(resultSet.getString("nome_perfil"));
			Usuario usuario = new Usuario();
			usuario.setId(resultSet.getLong("id_usuario"));
			usuario.setLogin(resultSet.getString("login_usuario"));
			usuario.setSenha(resultSet.getString("senha_usuario"));
			usuario.setPerfil(perfil);
			pessoaFisica = new PessoaFisica();
			pessoaFisica.setId(resultSet.getLong("id"));
			pessoaFisica.setCpf(resultSet.getString("cpf"));
			pessoaFisica.setNome(resultSet.getString("nome"));
			pessoaFisica.setDataNascimento(resultSet.getDate("datanascimento"));
			pessoaFisica.setSexo(resultSet.getString("sexo").charAt(0));
			pessoaFisica.setEstadoCivil(EstadoCivil.getEstadoCivil(resultSet
					.getString("estadocivil")));
			pessoaFisica.setDataCadastro(resultSet.getDate("datacadastro"));
			pessoaFisica.setUsuario(usuario);
			List<Endereco> enderecos = this
					.getEnderecosByIdPessoaFisica(pessoaFisica.getId());
			pessoaFisica.setEnderecos(enderecos);
			List<Telefone> telefones = this.telefoneDAOHelper
					.getByIdPessoa(pessoaFisica.getId());
			pessoaFisica.setTelefones(telefones);
			List<Email> emails = this.emailDAOHelper.getByIdPessoa(pessoaFisica
					.getId());
			pessoaFisica.setEmails(emails);
		}
		return pessoaFisica;
	}

	private List<Endereco> getEnderecosByIdPessoaFisica(long id)
			throws ClassNotFoundException, SQLException, IOException {
		PreparedStatement preparedStatement = JDBCConnectionFactory
				.getInstance().getConnection()
				.prepareStatement(SQL_GET_ENDERECOS_BY_ID_PESSOAFISICA);
		preparedStatement.setLong(1, id);
		ResultSet resultSet = preparedStatement.executeQuery();
		List<Long> idEnderecos = new ArrayList<Long>();
		while (resultSet.next()) {
			idEnderecos.add(resultSet.getLong("id_endereco"));
		}
		preparedStatement.close();
		resultSet.close();
		return this.getEnderecos(idEnderecos);
	}

	private List<Endereco> getEnderecos(List<Long> ids)
			throws ClassNotFoundException, SQLException, IOException {
		List<Endereco> enderecos = new ArrayList<Endereco>();
		for (Long id : ids) {
			Endereco endereco = this.enderecoDAOHelper.getById(id);
			enderecos.add(endereco);
		}
		return enderecos;
	}

}
